import fastifyBasicAuth from '@fastify/basic-auth'
import fastifyMultipart from '@fastify/multipart'
import { HttpStatus, LoggerService } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { NestFactory } from '@nestjs/core'
import {
  FastifyAdapter,
  type NestFastifyApplication
} from '@nestjs/platform-fastify'
import { WsAdapter } from '@nestjs/platform-ws'
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
import { isEqual } from 'lodash-unified'
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'

import { AppModule } from './app.module'
import { EXTRAS, MESSAGES } from './constants'
import { IConfig } from './interfaces'
import { ProxyUtil } from './utils'

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter(),
    { bufferLogs: true }
  )
  const loggerService = app.get<LoggerService>(WINSTON_MODULE_NEST_PROVIDER)
  const configService = app.get(ConfigService)
  const { apiPath, docPath, queuePath, port } =
    configService.get<IConfig['app']>('app')
  const basicAuth = configService.get<IConfig['basicAuth']>('basicAuth')
  const { LOCAL_IP } = EXTRAS

  app.enableCors()
  app.enableShutdownHooks()
  app.useLogger(loggerService)
  app.setGlobalPrefix(apiPath)
  await app.register(fastifyMultipart)
  await app.register(fastifyBasicAuth, {
    authenticate: true,
    validate: (username, password, req, res, done) => {
      if (isEqual({ username, password }, basicAuth)) {
        done()
      } else {
        done(new Error())
      }
    }
  })

  const instance = app.getHttpAdapter().getInstance()
  instance.addHook('onRequest', (req, res, done) => {
    // 对匹配的路径做鉴权
    if ([docPath, queuePath].includes(req.originalUrl)) {
      instance.basicAuth(req, res, err => {
        if (!err) {
          return done()
        }

        res.code(HttpStatus.UNAUTHORIZED).send({
          status: HttpStatus.UNAUTHORIZED,
          message: MESSAGES.BASIC_AUTH_NOT_CORRECT
        })
      })
    } else {
      done()
    }
  })

  // WebSocket
  const webSocketAdapter = new WsAdapter(app)
  app.useWebSocketAdapter(webSocketAdapter)

  /**
   * 只有配置了 0.0.0.0 才能用本机 ip 访问，否则只能用 localhost 或 127.0.0.1 访问
   * @see https://www.imooc.com/article/7581
   */
  const ADDRESS = '0.0.0.0'
  const LOGGER_CONTEXT = 'NestApplication'

  if (docPath) {
    const config = new DocumentBuilder()
      .setTitle('Nest Restful Fullstack Template')
      .setDescription('Nest Restful Fullstack 模板接口文档')
      .setVersion('1.0')
      .addBearerAuth()
      .build()
    const document = SwaggerModule.createDocument(app, config)
    SwaggerModule.setup(docPath, app, document)
  }

  await app.listen(port, ADDRESS, (err, address) => {
    const ADDR = address.replace(ADDRESS, LOCAL_IP)

    loggerService.log(`应用接口地址 ${ADDR}${apiPath}`, LOGGER_CONTEXT)
    loggerService.log(`应用任务队列 ${ADDR}${queuePath}`, LOGGER_CONTEXT)

    if (docPath) {
      loggerService.log(`接口文档地址 ${ADDR}${docPath}`, LOGGER_CONTEXT)
      loggerService.log(`接口文档数据 ${ADDR}${docPath}-json`, LOGGER_CONTEXT)
    }
  })
}
bootstrap()
